[BITS 16]
[org 0x7C00]

start:
    mov si,MSG
    call print_string
    call read_second_stage
    mov si,MSG_SECOND
    call print_string

    mov si, LOADING_KERNEL
    call print_string
    
    call read_kernel  
      
    ; The second stage is loaded into 0x0000:0x7E00 so we must set the ds to match
    mov ax, 0x00
    mov ds, ax
    mov ds, ax
    mov gs, ax
    mov ss, ax

    call check_a20
    ; ax contains the result of the a20 check
    cmp ax, 1
    je a20_is_enabled
    
    call enable_A20
a20_is_enabled:
        
    jmp 0x0000:0x07E00         ;Run the next stage
loop_forever:
    jmp $

; Function: check_a20
;
; Purpose: to check the status of the a20 line in a completely self-contained state-preserving way.
;          The function can be modified as necessary by removing push's at the beginning and their
;          respective pop's at the end if complete self-containment is not required.
;
; Returns: 0 in ax if the a20 line is disabled (memory wraps around)
;          1 in ax if the a20 line is enabled (memory does not wrap around)
 
check_a20:
    pushf
    push ds
    push es
    push di
    push si
 
    cli
 
    xor ax, ax ; ax = 0
    mov es, ax
 
    not ax ; ax = 0xFFFF
    mov ds, ax
 
    mov di, 0x0500
    mov si, 0x0510
 
    mov al, byte [es:di]
    push ax
 
    mov al, byte [ds:si]
    push ax
 
    mov byte [es:di], 0x00
    mov byte [ds:si], 0xFF
 
    cmp byte [es:di], 0xFF
 
    pop ax
    mov byte [ds:si], al
 
    pop ax
    mov byte [es:di], al
 
    mov ax, 0
    je check_a20__exit
 
    mov ax, 1
 
check_a20__exit:
    pop si
    pop di
    pop es
    pop ds
    popf
 
    ret
    
enable_A20: ; This is untested code and was cut and pasted :)
        cli
 
        call    a20wait
        mov     al,0xAD
        out     0x64,al
 
        call    a20wait
        mov     al,0xD0
        out     0x64,al
 
        call    a20wait2
        in      al,0x60
        push    eax
 
        call    a20wait
        mov     al,0xD1
        out     0x64,al
 
        call    a20wait
        pop     eax
        or      al,2
        out     0x60,al
 
        call    a20wait
        mov     al,0xAE
        out     0x64,al
 
        call    a20wait
        sti
        ret
 
a20wait:
        in      al,0x64
        test    al,2
        jnz     a20wait
        ret
 
 
a20wait2:
        in      al,0x64
        test    al,1
        jz      a20wait2
        ret

error_reset_too:
    mov si, ERROR_RESET_MSG
    call print_string
    jmp loop_forever
    retn
    
error_load_kernel:
    mov si, ERROR_LOADING_KERNEL
    call print_string
    jmp loop_forever
    retn
    
read_kernel:
    mov ah, 00h			; resets the drive
    mov dl, 80h
    int 13h			; this sets the cf on error but i'm not checking it
    jc error_reset_too    

    mov ah, 02h                ;When ah=, int13 reads a disk sector
    mov al, 0x35             ;Al is how many sectors to read
    mov ch, 0                  ;The track to read from
    mov cl, 22                  ;Sector Id -- this is the sector right after the second stage
    mov dh, 0                  ;Head
    mov dl, 80h                ;Drive (0 is floppy)
    mov bx, 0x1000            ;Es and Bx put together are where to load the program too (see jmp 0x01000:0x00)
    mov es, bx
    mov bx, 0x00
    int 13h                    ;Int 13 is all functions for disks

    jc error_load_kernel

    retn

error_reset:
    mov si, ERROR_RESET_MSG
    call print_string
    jmp loop_forever
    retn
    
error_load_stage:
    mov si, ERROR_LOADING_STAGE
    call print_string
    jmp loop_forever
    retn

read_second_stage:

    mov ah, 00h			; resets the drive
    mov dl, 80h
    int 13h			; this sets the cf on error but i'm not checking it
    jc error_reset    

    mov ah, 02h                ;When ah=, int13 reads a disk sector
    mov al, 0x30                ;Al is how many sectors to read
    mov ch, 0                  ;The track to read from
    mov cl, 2                  ;Sector Id
    mov dh, 0                  ;Head
    mov dl, 80h                ;Drive (0 is floppy)
    mov bx, 0x0000             ;Es and Bx put together are where to load the program too (see jmp 0x07e00:0x00)
    mov es, bx
    mov bx, 0x7E00
    int 13h                    ;Int 13 is all functions for disks

    jc error_load_stage

    retn

print_string:           ; Expects null terminated message in si
    mov al,[si]
    or al,al
    jz  .end
    inc si
    call print_char
    jmp print_string

.end:
    retn

print_char:
    mov ah,0x0E         ; Specifies that we want to write a character to the screen
    mov bl,0x07         ; Specifies output text color.  Not required, but useful to know
    mov bh,0x00         ; Page number.  Leave this alone.
    int 0x10            ; Signal video interrupt to BIOS
    retn

;data
    MSG db ' <--Welcome to the OS of D00m!',0x0A,0x0D,0
    MSG_SECOND db 'Load Stage 2', 0x0A, 0x0D, 0
    ERROR_RESET_MSG db 'Error 1', 0x0A, 0x0D, 0
    ERROR_LOADING_STAGE db 'Error 2', 0x0A, 0x0D, 0
    ERROR_LOADING_KERNEL db 'Error 3', 0x0A, 0x0D, 0
    LOADING_KERNEL db 'Load Kernel', 0x0A, 0x0D, 0


TIMES 510 - ($ - $$) db 0
DW 0xAA55
